/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ArkFile, ts, AstTreeUtils } from "arkanalyzer/lib";
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import Logger, { LOG_MODULE_TYPE } from "arkanalyzer/lib/utils/logger";
import { RuleListUtil } from "../../utils/common/DefectsList";
import { Defects, IssueReport } from "../../model/Defects";
import { Rule } from "../../model/Rule";
import { FileMatcher, MatcherCallback, MatcherTypes } from "../../Index";
import { RuleFix } from "../../model/Fix";

const logger = Logger.getLogger(
  LOG_MODULE_TYPE.HOMECHECK,
  "CommaDangleCheck"
);

interface CommaDangleOptions {
  arrays?: string;
  objects?: string;
  imports?: string;
  exports?: string;
  functions?: string;
  enums?: string;
  generics?: string;
  tuples?: string;
}

export class CommaDangleCheck implements BaseChecker {
  readonly metaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: "docs/comma-dangle-check.md",
    description: "Require or disallow trailing commas",
  };

  public issues: IssueReport[] = [];
  public defects: Defects[] = [];
  public rule: Rule;

  private fileMatcher: FileMatcher = {
    matcherType: MatcherTypes.FILE,
  };

  public registerMatchers(): MatcherCallback[] {
    const matchFileCb: MatcherCallback = {
      matcher: this.fileMatcher,
      callback: this.check,
    };
    return [matchFileCb];
  }

  private getRuleOption(type: keyof CommaDangleOptions): string {
    const option = this.rule.option?.[0];
    if (typeof option === 'string') {
      return option;
    }
    return typeof option === 'object' ? (option as CommaDangleOptions)[type] || 'never' : 'never';
  }

  public check = (target: ArkFile): void => {
    const code = target.getCode();
    const sourceFile = AstTreeUtils.getASTNode(target.getFilePath(), code);

    const visit = (node: ts.Node) => {
      if (ts.isArrayLiteralExpression(node)) {
        this.checkTrailingComma(node, target, 'arrays');
      } else if (ts.isObjectLiteralExpression(node)) {
        this.checkTrailingComma(node, target, 'objects');
      }
      
      if (ts.isEnumDeclaration(node)) {
        const ruleOption = this.getRuleOption('enums');
        this.checkEnumTrailingComma(node, target, ruleOption);
      }

      if (ts.isFunctionDeclaration(node) && node.typeParameters) {
        const ruleOption = this.getRuleOption('generics');
        if (ruleOption !== 'ignore') {
          this.checkGenericTrailingComma(node.typeParameters, target, ruleOption);
        }
      }

      if (ts.isTupleTypeNode(node)) {
        const ruleOption = this.getRuleOption('tuples');
        this.checkTupleTrailingComma(node, target, ruleOption);
      }

      if (ts.isImportDeclaration(node)) {
        const ruleOption = this.getRuleOption('imports');
        this.checkImportExportTrailingComma(node, target, ruleOption);
      }

      if (ts.isExportDeclaration(node)) {
        const ruleOption = this.getRuleOption('exports');
        this.checkImportExportTrailingComma(node, target, ruleOption);
      }

      if (ts.isCallExpression(node) || ts.isFunctionDeclaration(node) || ts.isNewExpression(node)) {
        const ruleOption = this.getRuleOption('functions');
        this.checkFunctionTrailingComma(node, target, ruleOption);
      }

      ts.forEachChild(node, visit);
    };

    ts.forEachChild(sourceFile, visit);
    
    this.issues.sort((a, b) => {
      if (a.defect.reportLine !== b.defect.reportLine) {
        return a.defect.reportLine - b.defect.reportLine;
      }
      return a.defect.reportColumn - b.defect.reportColumn;
    });
  };

  private findTrailingCommaPos(lastElement: ts.Node, node: ts.Node): { pos: number, column: number } {
    const sourceText = lastElement.getSourceFile().getFullText();
    let commaPos = -1;
    let column = -1;
    
    for (let i = lastElement.getEnd(); i < node.getEnd(); i++) {
      if (sourceText[i] === ',') {
        let foundClosing = false;
        for (let j = i + 1; j < node.getEnd(); j++) {
          if (!/[\s\r\n]/.test(sourceText[j])) {
            if (sourceText[j] === '}' || sourceText[j] === ']' || sourceText[j] === '>') {
              foundClosing = true;
            }
            break;
          }
        }
        if (foundClosing) {
          commaPos = i;
          const lineStart = sourceText.lastIndexOf('\n', i) + 1;
          column = i - lineStart + 1;
          break;
        }
      }
    }
    return { pos: commaPos, column };
  }

  private checkTrailingComma(
    node: ts.ArrayLiteralExpression | ts.ObjectLiteralExpression,
    arkFile: ArkFile,
    type: 'arrays' | 'objects'
  ): void {
    const elements = ts.isArrayLiteralExpression(node)
      ? node.elements
      : node.properties;
    
    if (elements.length === 0) return;

    const lastElement = elements[elements.length - 1];
    const sourceFile = lastElement.getSourceFile();
    const nodeText = node.getText();
    
    const hasTrailingComma = type === 'arrays' ? /,\s*\]/.test(nodeText) : /,\s*}/.test(nodeText);
    
    const lastElementPos = lastElement.getEnd();
    const closeBracketPos = node.getEnd();
    const lastElementLine = sourceFile.getLineAndCharacterOfPosition(lastElementPos).line;
    const closeBracketLine = sourceFile.getLineAndCharacterOfPosition(closeBracketPos).line;
    
    const isMultiline = closeBracketLine > lastElementLine;

    let violation = false;
    let message = "";

    switch (this.getRuleOption(type)) {
      case 'never':
        if (hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
      case 'always':
        if (!hasTrailingComma) {
          violation = true;
          message = "Missing trailing comma.";
        }
        break;
      case 'always-multiline':
        if (isMultiline && !hasTrailingComma) {
          violation = true;
          message = "Missing trailing comma.";
        } else if (!isMultiline && hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
      case 'only-multiline':
        if (!isMultiline && hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
    }

    if (violation) {
      const pos = lastElement.getEnd();
      const { line, character } = 
        sourceFile.getLineAndCharacterOfPosition(pos);

      const defect = new Defects(
        line + 1,
        character + 1,
        character + 2,
        message,
        this.metaData.severity,
        this.rule.ruleId,
        arkFile.getFilePath(),
        this.metaData.ruleDocPath,
        true,
        false,
        false
      );

      let fix: RuleFix;
      if (hasTrailingComma) {
        const commaInfo = this.findTrailingCommaPos(lastElement, node);
        if (commaInfo.pos !== -1) {
          fix = { range: [commaInfo.pos, commaInfo.pos + 1], text: '' };
        } else {
          fix = { range: [lastElement.getEnd(), lastElement.getEnd()], text: '' };
        }
      } else {
        fix = { range: [lastElement.getEnd(), lastElement.getEnd()], text: ',' };
      }

      this.issues.push(new IssueReport(defect, fix));
      RuleListUtil.push(defect);
    }
  }

  private checkEnumTrailingComma(
    node: ts.EnumDeclaration,
    arkFile: ArkFile,
    option: string
  ): void {
    if (node.members.length === 0) return;
    
    const lastMember = node.members[node.members.length - 1];
    const sourceFile = lastMember.getSourceFile();
    const enumText = node.getText();
    
    const enumBodyMatch = enumText.match(/{([^}]*)}/);
    if (!enumBodyMatch) return;
    
    const enumBody = enumBodyMatch[1].trim();
    const hasTrailingComma = /,\s*$/.test(enumBody);
    
    const openBracketPos = enumText.indexOf('{');
    const closeBracketPos = enumText.lastIndexOf('}');
    if (openBracketPos === -1 || closeBracketPos === -1) return;
    
    const fullStart = node.getStart();
    const openBracketLine = sourceFile.getLineAndCharacterOfPosition(fullStart + openBracketPos).line;
    const closeBracketLine = sourceFile.getLineAndCharacterOfPosition(fullStart + closeBracketPos).line;
    
    const isMultiline = closeBracketLine !== openBracketLine;

    let violation = false;
    let message = "";

    switch (option) {
      case 'never':
        if (hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
      case 'always':
        if (!hasTrailingComma) {
          violation = true;
          message = "Missing trailing comma.";
        }
        break;
      case 'always-multiline':
        if (isMultiline && !hasTrailingComma) {
          violation = true;
          message = "Missing trailing comma.";
        } else if (!isMultiline && hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
      case 'only-multiline':
        if (!isMultiline && hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
    }

    if (violation) {
      const enumStart = node.getStart();
      const { line, character } = sourceFile.getLineAndCharacterOfPosition(enumStart);
      
      const enumKeywordPos = node.getStart();
      const enumKeywordCol = sourceFile.getLineAndCharacterOfPosition(enumKeywordPos).character + 1;

      const defect = new Defects(
        line + 1,
        enumKeywordCol,
        enumKeywordCol + 1,
        message,
        this.metaData.severity,
        this.rule.ruleId,
        arkFile.getFilePath(),
        this.metaData.ruleDocPath,
        true,
        false,
        false
      );

      let fix: RuleFix;
      if (hasTrailingComma) {
        const commaInfo = this.findTrailingCommaPos(lastMember, node);
        if (commaInfo.pos !== -1) {
          fix = { range: [commaInfo.pos, commaInfo.pos + 1], text: '' };
        } else {
          fix = { range: [lastMember.getEnd(), lastMember.getEnd()], text: '' };
        }
      } else {
        fix = { range: [lastMember.getEnd(), lastMember.getEnd()], text: ',' };
      }

      this.issues.push(new IssueReport(defect, fix));
      RuleListUtil.push(defect);
    }
  }

  private checkGenericTrailingComma(
    typeParameters: ts.NodeArray<ts.TypeParameterDeclaration>,
    arkFile: ArkFile,
    option: string
  ): void {
    if (typeParameters.length === 0) return;
    
    const lastParam = typeParameters[typeParameters.length - 1];
    const sourceFile = lastParam.getSourceFile();
    
    const parent = typeParameters[0].parent;
    const parentText = parent.getText();
    
    const startBracket = parentText.indexOf('<');
    const endBracket = parentText.indexOf('>', startBracket);
    if (startBracket === -1 || endBracket === -1) return;
    
    const genericText = parentText.slice(startBracket + 1, endBracket);
    
    const hasTrailingComma = /,\s*$/.test(genericText.trim());
    const isMultiline = genericText.includes('\n');

    let violation = false;
    let message = "";

    switch (option) {
      case 'never':
        if (hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
      case 'always':
        if (!hasTrailingComma) {
          violation = true;
          message = "Missing trailing comma.";
        }
        break;
      case 'always-multiline':
        if (isMultiline && !hasTrailingComma) {
          violation = true;
          message = "Missing trailing comma.";
        } else if (!isMultiline && hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
      case 'only-multiline':
        if (!isMultiline && hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
    }

    if (violation) {
      const isFunctionDeclaration = ts.isFunctionDeclaration(parent);
      let reportLine: number;
      let reportColumn: number;
      
      if (isFunctionDeclaration) {
        const functionStart = parent.getStart();
        const genericStart = functionStart + startBracket;
        const { line, character } = sourceFile.getLineAndCharacterOfPosition(genericStart);
        reportLine = line + 1;
        reportColumn = character + 1;
      } else {
        const elementEnd = lastParam.getEnd();
        const { line, character } = sourceFile.getLineAndCharacterOfPosition(elementEnd);
        reportLine = line + 1;
        reportColumn = character + 1;
      }

      const defect = new Defects(
        reportLine,
        reportColumn,
        reportColumn + 1,
        message,
        this.metaData.severity,
        this.rule.ruleId,
        arkFile.getFilePath(),
        this.metaData.ruleDocPath,
        true,
        false,
        false
      );

      let fix: RuleFix;
      if (hasTrailingComma) {
        const commaInfo = this.findTrailingCommaPos(lastParam, parent);
        if (commaInfo.pos !== -1) {
          fix = { range: [commaInfo.pos, commaInfo.pos + 1], text: '' };
        } else {
          fix = { range: [lastParam.getEnd(), lastParam.getEnd()], text: '' };
        }
      } else {
        fix = { range: [lastParam.getEnd(), lastParam.getEnd()], text: ',' };
      }

      this.issues.push(new IssueReport(defect, fix));
      RuleListUtil.push(defect);
    }
  }

  private checkTupleTrailingComma(
    node: ts.TupleTypeNode,
    arkFile: ArkFile,
    option: string
  ): void {
    if (node.elements.length === 0) return;
    
    const lastElement = node.elements[node.elements.length - 1];
    const tupleText = node.getText();
    const hasTrailingComma = /,\s*\]/.test(tupleText);
    const isMultiline = this.isMultiline(node);

    let violation = false;
    let message = "";

    switch (option) {
      case 'never':
        if (hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
      case 'always':
        if (!hasTrailingComma) {
          violation = true;
          message = "Missing trailing comma.";
        }
        break;
      case 'always-multiline':
        if (isMultiline && !hasTrailingComma) {
          violation = true;
          message = "Missing trailing comma.";
        } else if (!isMultiline && hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
      case 'only-multiline':
        if (!isMultiline && hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
    }

    if (violation) {
      const sourceFile = lastElement.getSourceFile();
      const tupleStart = node.getStart();
      const { line, character } = sourceFile.getLineAndCharacterOfPosition(tupleStart);
      
      const bracketPos = node.getStart();
      const bracketCol = sourceFile.getLineAndCharacterOfPosition(bracketPos).character + 1;

      const defect = new Defects(
        line + 1,
        bracketCol,
        bracketCol + 1,
        message,
        this.metaData.severity,
        this.rule.ruleId,
        arkFile.getFilePath(),
        this.metaData.ruleDocPath,
        true,
        false,
        false
      );

      let fix: RuleFix;
      if (hasTrailingComma) {
        const commaInfo = this.findTrailingCommaPos(lastElement, node);
        if (commaInfo.pos !== -1) {
          fix = { range: [commaInfo.pos, commaInfo.pos + 1], text: '' };
        } else {
          fix = { range: [lastElement.getEnd(), lastElement.getEnd()], text: '' };
        }
      } else {
        fix = { range: [lastElement.getEnd(), lastElement.getEnd()], text: ',' };
      }

      this.issues.push(new IssueReport(defect, fix));
      RuleListUtil.push(defect);
    }
  }

  private hasTrailingComma(node: ts.Node, lastElement: ts.Node): boolean {
    const sourceText = node.getSourceFile().getFullText();
    const textBetween = sourceText.substring(lastElement.getEnd(), node.getEnd());
    return /,\s*[}>)\]]/.test(textBetween);
  }

  private isMultiline(node: ts.Node): boolean {
    const sourceFile = node.getSourceFile();
    let startPos: number;
    let endPos: number;
    let openBracketPos: number;
    let closeBracketPos: number;

    if (ts.isArrayLiteralExpression(node)) {
      if (node.elements.length === 0) return false;
      startPos = node.elements[0].getStart();
      endPos = node.elements[node.elements.length - 1].getEnd();
      openBracketPos = node.getStart();
      closeBracketPos = node.getEnd();
    } else if (ts.isObjectLiteralExpression(node)) {
      if (node.properties.length === 0) return false;
      startPos = node.properties[0].getStart();
      endPos = node.properties[node.properties.length - 1].getEnd();
      openBracketPos = node.getStart();
      closeBracketPos = node.getEnd();
    } else if (ts.isEnumDeclaration(node)) {
      if (node.members.length === 0) return false;
      startPos = node.members[0].getStart();
      endPos = node.members[node.members.length - 1].getEnd();
      openBracketPos = node.members.pos;
      closeBracketPos = node.members.end;
    } else if (ts.isTupleTypeNode(node)) {
      if (node.elements.length === 0) return false;
      startPos = node.elements[0].getStart();
      endPos = node.elements[node.elements.length - 1].getEnd();
      openBracketPos = node.getStart();
      closeBracketPos = node.getEnd();
    } else {
      return false;
    }

    const startLine = sourceFile.getLineAndCharacterOfPosition(startPos).line;
    const endLine = sourceFile.getLineAndCharacterOfPosition(endPos).line;
    const openBracketLine = sourceFile.getLineAndCharacterOfPosition(openBracketPos).line;
    const closeBracketLine = sourceFile.getLineAndCharacterOfPosition(closeBracketPos).line;

    return closeBracketLine !== openBracketLine || endLine !== startLine || startLine !== openBracketLine;
  }

  private checkImportExportTrailingComma(
    node: ts.ImportDeclaration | ts.ExportDeclaration,
    arkFile: ArkFile,
    option: string
  ): void {
    let elements: ts.NodeArray<ts.ImportSpecifier | ts.ExportSpecifier> | undefined;
    
    if (ts.isImportDeclaration(node)) {
      elements = node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings) 
        ? node.importClause.namedBindings.elements 
        : undefined;
    } else {
      elements = node.exportClause && ts.isNamedExports(node.exportClause) 
        ? node.exportClause.elements 
        : undefined;
    }

    if (!elements || elements.length === 0) return;

    const lastElement = elements[elements.length - 1];
    const hasTrailingComma = this.hasTrailingComma(node as any, lastElement);
    const isMultiline = this.isMultiline(node);

    this.checkAndReportViolation(option, hasTrailingComma, isMultiline, lastElement, arkFile);
  }

  private hasFunctionTrailingComma(
    node: ts.CallExpression | ts.FunctionDeclaration | ts.NewExpression,
    lastElement: ts.Node
  ): boolean {
    const sourceText = node.getSourceFile().getFullText();
    const textBetween = sourceText.substring(lastElement.getEnd(), node.getEnd());
    return /,\s*[)]/.test(textBetween);
  }

  private checkFunctionTrailingComma(
    node: ts.CallExpression | ts.FunctionDeclaration | ts.NewExpression,
    arkFile: ArkFile,
    option: string
  ): void {
    if (ts.isCallExpression(node)) {
      this.checkCallExpressionComma(node, arkFile, option);
      return;
    }

    if (ts.isNewExpression(node)) {
      this.checkNewExpressionComma(node, arkFile, option);
      return;
    }

    if (ts.isFunctionDeclaration(node)) {
      this.checkFunctionDeclarationComma(node, arkFile, option);
      return;
    }
  }

  private checkCallExpressionComma(
    node: ts.CallExpression,
    arkFile: ArkFile,
    option: string
  ): void {
    const args = node.arguments;
    if (args.length === 0) return;

    const lastArg = args[args.length - 1];
    
    if (ts.isObjectLiteralExpression(lastArg)) {
      this.checkTrailingComma(lastArg, arkFile, 'objects');
      return;
    }
    
    const hasTrailingComma = this.hasFunctionTrailingComma(node, lastArg);
    const isMultiline = this.isMultiline(node);
    
    this.checkAndReportViolation(option, hasTrailingComma, isMultiline, lastArg, arkFile);
  }

  private checkNewExpressionComma(
    node: ts.NewExpression,
    arkFile: ArkFile,
    option: string
  ): void {
    const args = node.arguments;
    if (!args || args.length === 0) return;

    const lastArg = args[args.length - 1];
    const hasTrailingComma = this.hasFunctionTrailingComma(node, lastArg);
    const isMultiline = this.isMultiline(node);
    
    this.checkAndReportViolation(option, hasTrailingComma, isMultiline, lastArg, arkFile);
  }

  private checkFunctionDeclarationComma(
    node: ts.FunctionDeclaration,
    arkFile: ArkFile,
    option: string
  ): void {
    const functionText = node.getText();
    const paramMatch = functionText.match(/\(([^)]*)\)/);
    
    if (paramMatch && paramMatch[1].trim()) {
      const paramText = paramMatch[1].trim();
      const lastParamMatch = paramText.match(/,\s*$/);
      
      const parameters = node.parameters;
      if (parameters.length > 0) {
        const lastParam = parameters[parameters.length - 1];
        const hasTrailingComma = lastParamMatch !== null;
        const isMultiline = paramText.includes('\n');
        
        this.checkAndReportViolation(option, hasTrailingComma, isMultiline, lastParam, arkFile);
      }
    }
  }

  private checkAndReportViolation(
    option: string,
    hasTrailingComma: boolean,
    isMultiline: boolean,
    lastElement: ts.Node,
    arkFile: ArkFile
  ): void {
    let violation = false;
    let message = "";
    switch (option) {
      case "never":
        if (hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
      case "always":
        if (!hasTrailingComma) {
          violation = true;
          message = "Missing trailing comma.";
        }
        break;
      case "always-multiline":
        if (isMultiline && !hasTrailingComma) {
          violation = true;
          message = "Missing trailing comma.";
        } else if (!isMultiline && hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
      case "only-multiline":
        if (!isMultiline && hasTrailingComma) {
          violation = true;
          message = "Unexpected trailing comma.";
        }
        break;
    }

    if (violation) {
      const sourceFile = lastElement.getSourceFile();
      const pos = lastElement.getEnd();
      let line = sourceFile.getLineAndCharacterOfPosition(pos).line;
      let column = 1;
      
      const isInFunctionParams = ts.isParameter(lastElement) || 
        (lastElement.parent && (ts.isCallExpression(lastElement.parent) || ts.isNewExpression(lastElement.parent)));
      
      if (isInFunctionParams) {
        const hasObjectLiteral = ts.isObjectLiteralExpression(lastElement) || 
          (ts.isParameter(lastElement) && lastElement.initializer && ts.isObjectLiteralExpression(lastElement.initializer));
        
        if (hasObjectLiteral) {
          const objLiteral = ts.isObjectLiteralExpression(lastElement) ? lastElement : 
            (lastElement.initializer as ts.ObjectLiteralExpression);
          if (objLiteral.properties.length > 0) {
            const lastProp = objLiteral.properties[objLiteral.properties.length - 1];
            const propEnd = lastProp.getEnd();
            const { line: propLine, character: propChar } = sourceFile.getLineAndCharacterOfPosition(propEnd);
            line = propLine;
            column = propChar + 1;
          }
        } else {
          const { character } = sourceFile.getLineAndCharacterOfPosition(lastElement.getEnd());
          column = character + 1;
        }
      } else if (hasTrailingComma) {
        const commaInfo = this.findTrailingCommaPos(lastElement, lastElement.parent);
        if (commaInfo.pos !== -1) {
          line = sourceFile.getLineAndCharacterOfPosition(commaInfo.pos).line;
          column = commaInfo.column;
        }
      } else {
        const { character } = sourceFile.getLineAndCharacterOfPosition(pos);
        column = character + 1;
      }

      const defect = new Defects(
        line + 1,
        column,
        column + 1,
        message,
        this.metaData.severity,
        this.rule.ruleId,
        arkFile.getFilePath(),
        this.metaData.ruleDocPath,
        true,
        false,
        false
      );

      let fix: RuleFix;
      if (hasTrailingComma) {
        const commaInfo = this.findTrailingCommaPos(lastElement, lastElement.parent);
        if (commaInfo.pos !== -1) {
          fix = { range: [commaInfo.pos, commaInfo.pos + 1], text: '' };
        } else {
          fix = { range: [lastElement.getEnd(), lastElement.getEnd()+1], text: '' };
        }
      } else {
        fix = { range: [lastElement.getEnd(), lastElement.getEnd()], text: ',' };
      }

      this.issues.push(new IssueReport(defect, fix));
      RuleListUtil.push(defect);
    }
  }
}
